/*
 * Copyright 2016, The Android Open Source Project
 * Copyright (c) 2017-2018 Spotify AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.ohos.architecture.blueprints.todoapp.data.source.local;

import ohos.app.Context;
import ohos.utils.zson.ZSONObject;

import com.example.ohos.architecture.blueprints.todoapp.data.Task;
import com.example.ohos.architecture.blueprints.todoapp.data.TaskDetails;
import com.example.ohos.architecture.blueprints.todoapp.data.TaskDisc;
import com.example.ohos.architecture.blueprints.todoapp.data.source.TasksDataSource;
import com.example.ohos.architecture.blueprints.todoapp.util.LogUtil;
import com.example.ohos.architecture.blueprints.todoapp.util.PreferencesUtil;
import com.example.ohos.architecture.blueprints.todoapp.util.schedulers.BaseSchedulerProvider;
import com.google.common.base.Optional;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Concrete implementation of a data source as a db
 *
 * @since 2021-07-12
 */
public class TasksLocalDataSource implements TasksDataSource {
    /**
     * TASKLISTS
     */
    public static final String TASKLISTS = "tasklists";
    private static TasksLocalDataSource INSTANCE;

    // Prevent direct instantiation.
    private TasksLocalDataSource(Context context, BaseSchedulerProvider schedulerProvider) {
        checkNotNull(context, "context cannot be null");
        checkNotNull(schedulerProvider, "scheduleProvider cannot be null");
    }

    private Task getTask(TaskDisc values) {
        String itemId = values.getId();
        String title = values.getTitle();
        String description = values.getDescription();
        boolean completed = values.isCompleted();
        TaskDetails details = TaskDetails.builder().title(title).description(description).completed(completed).build();
        return Task.create(itemId, details);
    }

    /**
     * getInstance
     *
     * @param context context
     * @param schedulerProvider schedulerProvider
     * @return TasksLocalDataSource TasksLocalDataSource
     */
    public static TasksLocalDataSource getInstance(
        Context context, BaseSchedulerProvider schedulerProvider) {
        if (INSTANCE == null) {
            INSTANCE = new TasksLocalDataSource(context, schedulerProvider);
        }
        return INSTANCE;
    }

    /**
     * destroyInstance
     */
    public static void destroyInstance() {
        INSTANCE = null;
    }

    @Override
    public Flowable<List<Task>> getTasks() {
        Observable<List<Task>> listObservable = Observable.create(new ObservableOnSubscribe<List<Task>>() {
            @Override
            public void subscribe(@io.reactivex.annotations.NonNull ObservableEmitter<List<Task>> emitter) throws Exception {
                List<Task> taskList = new ArrayList<>();
                Set<String> oldValue = PreferencesUtil.getStringArray(TASKLISTS);
                Iterator<String> it = oldValue.iterator();
                while (it.hasNext()) {
                    String value = it.next();
                    ZSONObject zsonObject = ZSONObject.stringToZSON(value);
                    Task task = getTask(new TaskDisc(zsonObject.getString("id"), zsonObject.getString("title"),
                        zsonObject.getString("description"), zsonObject.getBoolean("completed")));
                    taskList.add(task);
                }
                emitter.onNext(taskList);
            }
        });
        return listObservable.toFlowable(BackpressureStrategy.BUFFER);
    }

    @Override
    public Flowable<Optional<Task>> getTask(String taskId) {
        return null;
    }

    @Override
    public void saveTask(Task task) {
        try {
            checkNotNull(task);
            Set<String> oldValue = PreferencesUtil.getStringArray(TASKLISTS);
            Iterator<String> it = oldValue.iterator();
            Set<String> newSet = new HashSet<>();
            String tempTask = ZSONObject.classToZSON(new TaskDisc(task.id(), task.details().title(),
                task.details().description(), task.details().completed())).toString();
            boolean isHave = false;
            while (it.hasNext()) {
                String value = it.next();
                ZSONObject zsonObject = ZSONObject.stringToZSON(value);
                String id = zsonObject.getString("id");
                String title = zsonObject.getString("title");
                boolean completed = zsonObject.getBoolean("completed");
                if (id.equals(task.id())) {
                    isHave = true;
                    zsonObject.put("completed", task.details().completed());
                    zsonObject.put("title", task.details().title());
                    zsonObject.put("description", task.details().description());
                }
                newSet.add(zsonObject.toString());
            }

            if (!isHave) {
                newSet.add(tempTask);
            }
            PreferencesUtil.saveStringArray(TASKLISTS, newSet);
        } catch (Exception ex) {
            LogUtil.debug("MOBIUSTag", "saveError:" + ex.toString());
        }
    }

    @Override
    public void deleteAllTasks() {
        PreferencesUtil.saveStringArray(TASKLISTS, new HashSet<>());
    }

    @Override
    public void deleteTask(String taskId) {
        Set<String> oldValue = PreferencesUtil.getStringArray(TASKLISTS);
        Iterator<String> it = oldValue.iterator();
        String needDeletetask = "";
        while (it.hasNext()) {
            String value = it.next();
            ZSONObject zsonObject = ZSONObject.stringToZSON(value);
            TaskDisc taskDisc = new TaskDisc(zsonObject.getString("id"), zsonObject.getString("title"),
                zsonObject.getString("description"), zsonObject.getBoolean("completed"));
            if (taskDisc.getId().equals(taskId)) {
                needDeletetask = value;
            }
        }

        if (!needDeletetask.isEmpty()) {
            oldValue.remove(needDeletetask);
        }
        PreferencesUtil.saveStringArray(TASKLISTS, oldValue);
    }
}
